﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Model;
using System.ServiceModel;
using Common;
using Common.Interfaces;
using System.Windows.Forms;
using JOthello.Forms;
using Common.Extensions;
using System.Threading;

namespace JOthello.Controls
{
    [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
    public class MultiplayerControl : IJOthelloCallback
    {
        IJOthello srv = null;
        CSpieler me = null;
        CSpieler opponent = null;
        string spielID = "";
        bool canceled = false;

        MultiplayerForm MultiplayerForm;
        SpielForm SpielForm;
        WantsToPlayForm WantsToPlayForm;
        WaitForm WaitForm;

        List<CSpieler> activeSpieler = new List<CSpieler>();

        System.Windows.Forms.Timer getOnlineSpieler;

        public MultiplayerControl(MultiplayerForm form, CSpieler spieler)
        {
            me = spieler;
            this.MultiplayerForm = form;

            getOnlineSpieler = new System.Windows.Forms.Timer();
            getOnlineSpieler.Interval = 1000;
            getOnlineSpieler.Tick += (s, e) => reloadSpieler();

            MultiplayerForm.BtnConnect.Click += new EventHandler(BtnConnect_Click);
            MultiplayerForm.FormClosed += (s, e) => disconnect();
            MultiplayerForm.Disposed += (s, e) => disconnect();
            MultiplayerForm.DgvSpielerAll.CellClick += new DataGridViewCellEventHandler(DgvSpielerAll_CellClick);
            MultiplayerForm.KeyDown += (s, e) =>
            {
                if (e.KeyCode == Keys.F5 || (e.KeyCode == Keys.R && e.Control))
                {
                    reloadSpieler();
                }
            };

            MultiplayerForm.NudPort.Value = Properties.Settings.Default.MultiplayerPort;
            MultiplayerForm.TxtSrv.Text = Properties.Settings.Default.MultiplayerHost;
            MultiplayerForm.BtnConnect.Focus();
        }

        #region events

        private void BtnConnect_Click(object sender, EventArgs e)
        {
            MultiplayerForm.BtnConnect.Enabled =
                MultiplayerForm.TxtSrv.Enabled =
                MultiplayerForm.NudPort.Enabled =
                    false;
            MultiplayerForm.Cursor = 
                MultiplayerForm.DgvSpielerAll.Cursor = 
                    Cursors.WaitCursor;
            try
            {
                bool changed = false;
                if (Properties.Settings.Default.MultiplayerPort != (int)MultiplayerForm.NudPort.Value)
                {
                    Properties.Settings.Default.MultiplayerPort = (int)MultiplayerForm.NudPort.Value;
                    changed = true;
                }
                if (Properties.Settings.Default.MultiplayerHost != MultiplayerForm.TxtSrv.Text)
                {
                    Properties.Settings.Default.MultiplayerHost = MultiplayerForm.TxtSrv.Text;
                    changed = true;
                }
                if (changed) Properties.Settings.Default.Save();

                NetTcpBinding binding = new NetTcpBinding();
                binding.Security.Mode = SecurityMode.None;
                binding.MaxReceivedMessageSize =
                    binding.ReaderQuotas.MaxArrayLength =
                    binding.ReaderQuotas.MaxBytesPerRead =
                        Int32.MaxValue;
                var factory = new DuplexChannelFactory<IJOthello>(this, binding, new EndpointAddress("net.tcp://" + Properties.Settings.Default.MultiplayerHost + ":" + Properties.Settings.Default.MultiplayerPort.ToString() + "/JOthello"));
                srv = factory.CreateChannel();

                srv.connect(me);
                if (!srv.register(me))
                {
                    MessageBox.Show("Es scheint bereits ein Spieler mit diesem Namen auf diesem Server angemeldet zu sein oder Sie befinden sich auf der Blacklist.", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    MultiplayerForm.Close();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                MultiplayerForm.BtnConnect.Enabled =
                    MultiplayerForm.TxtSrv.Enabled =
                    MultiplayerForm.NudPort.Enabled =
                        true;
                MultiplayerForm.Cursor =
                    MultiplayerForm.DgvSpielerAll.Cursor =
                        Cursors.Default;
                return;
            }

            MultiplayerForm.Cursor =
                MultiplayerForm.DgvSpielerAll.Cursor =
                    Cursors.Default;
            
            reloadSpieler();

            getOnlineSpieler.Start();
        }
        private void DgvSpielerAll_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            getOnlineSpieler.Stop();
            if (e.ColumnIndex == 2)
            {
                try
                {
                    srv.playAgainst(me, activeSpieler[e.RowIndex]);
                    opponent = activeSpieler[e.RowIndex];
                    WaitForm = new WaitForm("Auf Gegner warten", "JOthello | Multiplayer", true);
                    if (DialogResult.Cancel == WaitForm.ShowDialog())
                    {
                        cancelRequest();
                    }
                }
                catch (Exception)
                {
                    MessageBox.Show("Der Server reagiert nicht mehr :(", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    this.disconnect();
                }
            }
            getOnlineSpieler.Start();
        }
        #endregion

        private void reloadSpieler()
        {
            try
            {
                if (srv == null) return;

                activeSpieler = srv.getRegistered();
                activeSpieler.Remove(me);

                MultiplayerForm.DgvSpielerAll.Rows.Clear();

                activeSpieler.ForEach((sp) => MultiplayerForm.DgvSpielerAll.Rows.Add(new object[] { sp.Bild.Resize(50, 50), sp.Name, "Spiel starten" }));
            }
            catch (Exception ex)
            {
                getOnlineSpieler.Stop();
                if (ex is CommunicationObjectFaultedException)
                {
                    MessageBox.Show("Der Server reagiert nicht mehr :(", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    MessageBox.Show(ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                this.MultiplayerForm.Close();
            }
        }

        private void doTurn(CPoint p)
        {
            try
            {
                srv.turn(spielID, me, p, opponent);
            }
            catch (Exception)
            {
                MessageBox.Show("Der Server scheint nicht mehr erreichbar zu sein :(", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                canceled = true;
                this.gameCanceled();
            }
        }

        private void disconnect()
        {
            try
            {
                if (srv == null) return;

                cancelRequest();
                srv.unregister(me);
                srv.disconnect(me);
                srv = null;
            }
            catch (Exception) { }
        }

        private void cancelRequest()
        {
            try
            {
                canceled = true;
                if (this.WaitForm != null && this.WaitForm.Visible && !this.WaitForm.IsDisposed)
                {
                    this.WaitForm.finish();
                }
                if (opponent != null && srv != null)
                {
                    srv.cancelGame(spielID, me, opponent);
                    opponent = null;
                }
            }
            catch (Exception) { return; }
        }

        #region srv

        public void askToPlay(CSpieler spieler)
        {
            getOnlineSpieler.Stop();
            bool accepted = DialogResult.Yes == (WantsToPlayForm = new WantsToPlayForm(spieler)).ShowDialog();
            srv.acceptPlayAgainst(accepted, me, spieler);
            if (!accepted)
                getOnlineSpieler.Start();
        }

        public void playAccepted(bool accepted, CSpieler opposite)
        {
            if (this.WaitForm != null && this.WaitForm.Visible && !this.WaitForm.IsDisposed)
            {
                this.WaitForm.finish();
            }
            if (accepted)
            {
                srv.startGame(me, opposite, Properties.Settings.Default.FieldSize, Properties.Settings.Default.ShowPossible, CSpielfeld.GameMode.Default);
            }
            else if(opponent != null)
            {
                MessageBox.Show(opposite.Name + " hat deine Spielaufforderung leider nicht akzeptiert.", ":(", MessageBoxButtons.OK, MessageBoxIcon.Information);
                getOnlineSpieler.Start();
            }
        }

        public void gotTurn(CSpieler sp, CPoint p) {
            if (SpielForm.InvokeRequired)
                SpielForm.Invoke((MethodInvoker)delegate{ this.SpielForm.SpielControl.gotNextTurn(p); });
            else
                this.SpielForm.SpielControl.gotNextTurn(p);
        }

        public void startGame(string spielID, bool isYourTurn, CSpieler opponent, CSpielfeld.FieldColor yourColor, int size, bool showPossible, CSpielfeld.GameMode m)
        {
            getOnlineSpieler.Stop();
            me.Farbe = yourColor;
            canceled = false;
            this.spielID = spielID;
            this.opponent = opponent;
            this.SpielForm = new SpielForm(new List<CSpieler>() { me, opponent }, m, size, showPossible, doTurn, opponent, (isYourTurn ? me : opponent));
            this.SpielForm.Closed += (s, e) => cancelRequest();
            this.SpielForm.ShowDialog();
            getOnlineSpieler.Start();
        }

        public void gotKicked()
        {
            getOnlineSpieler.Stop();
            canceled = true;
            MessageBox.Show("Du wurdest vom Server gekickt :(", "Kicked out", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            if (this.SpielForm != null && !this.SpielForm.IsDisposed) this.SpielForm.Close();
            this.MultiplayerForm.Close();
        }

        public void gameCanceled()
        {
            if(!canceled)
                MessageBox.Show("Das Spiel wurde durch den Gegner abgebrochen :(");
            
            if (this.SpielForm != null && this.SpielForm.Visible && !this.SpielForm.IsDisposed)
            {
                this.SpielForm.Close();
            }
            if (this.WantsToPlayForm != null && this.WantsToPlayForm.Visible && !this.WantsToPlayForm.IsDisposed)
            {
                this.WantsToPlayForm.Close();
            }
            if (this.WaitForm != null && this.WaitForm.Visible && !this.WaitForm.IsDisposed)
            {
                this.WaitForm.finish();
            }
            getOnlineSpieler.Start();
        }
        #endregion
    }
}
